/*!
   \file

   \brief
      Windows Platform source file. Implements functionality in Platform.hpp/PlatformInterface.hpp
      on Windows.

   © 2011 Mark W. Gabby II

   This file is part of Rock Hunter.

   Rock Hunter is free software: you can redistribute it and/or modify it under the terms of the
   GNU General Public License as published by the Free Software Foundation, either version 3 of the
   License, or (at your option) any later version.

   Rock Hunter is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
   even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along with Rock Hunter.
   If not, see <http://www.gnu.org/licenses/>.
*/
#include "Systems/Platform/Platform.hpp"

#include "Systems/ErrorManager/ErrorManager.hpp"

#ifndef WINVER
#define WINVER 0x0501 // Windows XP
#endif
#include <Shlobj.h>

#include "PlatformTypesWin.hpp"
#include "ScopedOpenGLContext.hpp"
#include "Systems/Platform/glCP.hpp"


WinPFDataStruct gPFData;

namespace pf
{
void MaybeInitialize();
}

namespace iPlatform
{
tPixelFormatIndex GetPixelFormatCount()
{
   pf::MaybeInitialize();

   return gPFData.SupportedPixelFormats.size();
}

PixelFormat GetPixelFormat( tPixelFormatIndex FormatIndex )
{
   pf::MaybeInitialize();

   return gPFData.SupportedPixelFormats[FormatIndex];
}

tMonitorIndex GetMonitorCount()
{
   pf::MaybeInitialize();

   return gPFData.MonitorInfos.size();
}

MonitorInfo GetMonitorInformation( tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();

   return gPFData.MonitorInfos[MonitorIndex];
}

uint16_t GetMonitorWidth( tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();

   HMONITOR MonitorHandle = gPFData.MonitorHandles[MonitorIndex];

   MONITORINFOEX WinMonitorInfo;
   WinMonitorInfo.cbSize = sizeof( MONITORINFOEX );
   ::GetMonitorInfo( MonitorHandle, &WinMonitorInfo );

   return WinMonitorInfo.rcMonitor.right - WinMonitorInfo.rcMonitor.left;
}

uint16_t GetMonitorHeight( tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();

   HMONITOR MonitorHandle = gPFData.MonitorHandles[MonitorIndex];

   MONITORINFOEX WinMonitorInfo;
   WinMonitorInfo.cbSize = sizeof( MONITORINFOEX );
   ::GetMonitorInfo( MonitorHandle, &WinMonitorInfo );

   return WinMonitorInfo.rcMonitor.bottom - WinMonitorInfo.rcMonitor.top;
}

uint16_t GetMonitorWorkingWidth( tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();
   HMONITOR MonitorHandle = gPFData.MonitorHandles[MonitorIndex];

   MONITORINFOEX WinMonitorInfo;
   WinMonitorInfo.cbSize = sizeof( MONITORINFOEX );
   ::GetMonitorInfo( MonitorHandle, &WinMonitorInfo );

   return WinMonitorInfo.rcWork.right - WinMonitorInfo.rcWork.left;
}

uint16_t GetMonitorWorkingHeight( tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();

   HMONITOR MonitorHandle = gPFData.MonitorHandles[MonitorIndex];

   MONITORINFOEX WinMonitorInfo;
   WinMonitorInfo.cbSize = sizeof( MONITORINFOEX );
   ::GetMonitorInfo( MonitorHandle, &WinMonitorInfo );

   return WinMonitorInfo.rcWork.bottom - WinMonitorInfo.rcWork.top;
}

int16_t GetMonitorLeftCoordinate( tMonitorIndex MonitorIndex )
{
   HMONITOR MonitorHandle = gPFData.MonitorHandles[MonitorIndex];

   MONITORINFOEX WinMonitorInfo;
   WinMonitorInfo.cbSize = sizeof( MONITORINFOEX );
   ::GetMonitorInfo( MonitorHandle, &WinMonitorInfo );

   return WinMonitorInfo.rcMonitor.left;
}
int16_t GetMonitorTopCoordinate( tMonitorIndex MonitorIndex )
{
   HMONITOR MonitorHandle = gPFData.MonitorHandles[MonitorIndex];

   MONITORINFOEX WinMonitorInfo;
   WinMonitorInfo.cbSize = sizeof( MONITORINFOEX );
   ::GetMonitorInfo( MonitorHandle, &WinMonitorInfo );

   return WinMonitorInfo.rcMonitor.top;
}

uint8_t GetMonitorRefreshRate( tMonitorIndex MonitorIndex )
{
   std::map<tMonitorIndex, tSystemMonitorIndex>::iterator it;
   it = gPFData.MonitorIndexToSystemMonitorIndex.find( MonitorIndex );
   if ( it == gPFData.MonitorIndexToSystemMonitorIndex.end() )
   {
      return 0;
   }
   tSystemMonitorIndex SystemMonitorIndex = ( *it ).second;

   // Get the monitor device information
   DISPLAY_DEVICE Device = { 0 };
   // Set it up so it's ready for use.
   Device.cb = sizeof( DISPLAY_DEVICE );
   EnumDisplayDevices( NULL, SystemMonitorIndex, &Device, 0 );

   DEVMODE CurrentDevMode;
   CurrentDevMode.dmSize = sizeof( CurrentDevMode );
   CurrentDevMode.dmDriverExtra = 0;
   EnumDisplaySettings( Device.DeviceName, ENUM_CURRENT_SETTINGS, &CurrentDevMode );

   AssertError( CurrentDevMode.dmDisplayFrequency <= 255, "Avion engine bug. Refresh rate higher than "
                "255, update refresh rate values to be more than a byte." );

   return ( uint8_t )CurrentDevMode.dmDisplayFrequency;
}

uint16_t GetBorderWidth()
{
   return GetSystemMetrics( SM_CXFIXEDFRAME );
}

uint16_t GetBorderHeight()
{
   return GetSystemMetrics( SM_CYFIXEDFRAME );
}

uint16_t GetMenuHeight()
{
   return GetSystemMetrics( SM_CYSIZE );
}

tScreenModeIndex GetScreenModesCount( tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();

   return gPFData.ModesPerMonitor[MonitorIndex];
}

ScreenModeInfo GetScreenModeInfo( tScreenModeIndex ModeIndex, tMonitorIndex MonitorIndex )
{
   pf::MaybeInitialize();

   tModeAndMonitor ModeAndMonitor;
   ModeAndMonitor.first = ModeIndex;
   ModeAndMonitor.second = MonitorIndex;

   return gPFData.ModeAndMonitorToScreenMode[ModeAndMonitor];
}
}

namespace iInput
{
ControllerInfo GetControllerInfo( const tControllerIndex ControllerIndex )
{
   pf::MaybeInitialize();

   return gPFData.ControllerInfos[ControllerIndex];
}
tControllerIndex GetControllerCount()
{
   pf::MaybeInitialize();

   return gPFData.ControllerInfos.size();
}

bool CaptureController( const tControllerIndex ControllerIndex, const tWindowID WindowID )
{
   return gPFData.Windows[WindowID]->CaptureController( ControllerIndex );
}

tControllerIndex GetCapturedControllerIndex( const tWindowID WindowID )
{
   return gPFData.Windows[WindowID]->GetControllerIndex();
}

bool ReleaseController( const tWindowID WindowID )
{
   return gPFData.Windows[WindowID]->ReleaseController();
}
}

namespace pf
{
/*!
   \brief
      Returns true if this a supported pixel format, false otherwise.
   A supported format supports enough features, for example, the right number of bits per pixel,
   support for the depth buffer, and double buffering.

   \param FormatIndex
      System format index of pixel format to check.
   \param Format
      Engine-localized version of the format created from system version.
   \param PreviousContext
      If this was the current context when the function starts, it will be restored as such when
      the function ends.

   \return
      True if the format is valid, false otherwise.
*/
bool IsSupportedPixelFormat( const tSystemPixelFormatIndex FormatIndex, const PixelFormat & Format,
                             ScopedOpenGLContext & PreviousContext )
{
   // Get through the gauntlet. If you do, you're valid.
   if ( Format.acceleration != kFull )
   {
      return false;
   }

   // We want 16 or 32 bits per pixel.
   if ( ( Format.bitsPerPixel != 32 ) && ( Format.bitsPerPixel != 24 ) && ( Format.bitsPerPixel != 16 ) )
   {
      return false;
   }

   if ( ( Format.bitPerColor[kA] > 1 ) == false )
   {
      return false;
   }

   // We need SOME form of depth buffer.
   if ( Format.depthBufferBits == 0 )
   {
      return false;
   }

   // Double-buffering is a must.
   if ( Format.doubleBuffered == false )
   {
      return false;
   }

   // If this format can't draw to a window, then we can't use it.
   if ( Format.glWindowSupported == false )
   {
      return false;
   }

   // We don't want a color indexed mode.
   if ( Format.RGBA == false )
   {
      return false;
   }

   const bool PreviousWasCurrent = PreviousContext.IsCurrent();

   // Check it for supported features
   ScopedOpenGLContext * ScopedContext = new ScopedOpenGLContext();
   ScopedContext->Init( FormatIndex );
   CrossPlatformOpenGL::Initialize();
   const bool EnoughFeatures = CrossPlatformOpenGL::SufficientFeaturesSupported();
   delete ScopedContext;
   ScopedContext = NULL;

   if ( PreviousWasCurrent )
   {
      PreviousContext.MakeCurrent();
   }
   return EnoughFeatures;
}

bool IsSupportedScreenMode( const ScreenModeInfo & Mode )
{
   // Get through the gauntlet. If you do, you're valid.
   if ( Mode.bitsPerPixel != 16 && Mode.bitsPerPixel != 32 )
   {
      return false;
   }

   if ( Mode.dimensions[kWidth] < 320 )
   {
      return false;
   }

   if ( Mode.dimensions[kHeight] < 200 )
   {
      return false;
   }

   if ( Mode.refreshRate < 59 )
   {
      return false;
   }

   return true;
}

BOOL CALLBACK EnumerateMonitors( HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData )
{
   // Get the monitor info for this monitor
   MONITORINFOEX MonitorInfoEx;
   MonitorInfoEx.cbSize = sizeof( MonitorInfoEx );
   ::GetMonitorInfo( hMonitor, &MonitorInfoEx );

   // Find out which of our current monitors this handle refers to
   const tMonitorIndex MonitorCount = iPlatform::GetMonitorCount();
   for ( tMonitorIndex MonitorIndex = 0; MonitorIndex < MonitorCount; ++MonitorIndex )
   {
      const MonitorInfo & Info = gPFData.MonitorInfos[MonitorIndex];

      if ( strcmp( MonitorInfoEx.szDevice, Info.name.c_str() ) == 0 )
      {
         gPFData.MonitorHandles[MonitorIndex] = hMonitor;
      }
   }

   return TRUE;
}

bool InitializeMonitorInfosList()
{
   gPFData.MonitorInfos.clear();
   gPFData.MonitorIndexToSystemMonitorIndex.clear();

   // Get the structure that will be used to call the function.
   DISPLAY_DEVICE device = { 0 };
   // Set it up so it's ready for use.
   device.cb = sizeof( DISPLAY_DEVICE );

   // For each monitor ...
   uint8_t deviceIndex = 0;

   while ( EnumDisplayDevices( NULL, deviceIndex, &device, 0 ) )
   {
      MonitorInfo CurMonitor;

      CurMonitor.name = device.DeviceName;
      CurMonitor.string = device.DeviceString;

      // Check to see if this is the primary device. If so, set the
      // 'primary' flag to true.
      CurMonitor.valid = false;
      CurMonitor.primary = false;
      if ( device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE )
      {
         CurMonitor.valid = true;
         CurMonitor.primary = true;
      }
      // Check to see if this monitor is active and not mirroring another monitor. If so, set the
      // 'valid' flag to true.
      else if ( ( device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP ) &&
                !( device.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER ) )
      {
         CurMonitor.valid = true;
      }

      if ( CurMonitor.valid )
      {
         const int CurMonitorIndex = gPFData.MonitorInfos.size();
         gPFData.MonitorInfos.push_back( CurMonitor );
         gPFData.MonitorIndexToSystemMonitorIndex[CurMonitorIndex] = deviceIndex;
      }

      ++deviceIndex;
   }

   gPFData.MonitorHandles.resize( gPFData.MonitorInfos.size() );
   EnumDisplayMonitors( NULL, NULL, EnumerateMonitors, 0 );

   // No monitors found. That's not good.
   if ( gPFData.MonitorInfos.size() > 0 )
   {
      return true;
   }
   return false;
}

bool InitializeScreenModesList()
{
   gPFData.ModeAndMonitorToSystemScreenModeIndex.clear();
   gPFData.ModeAndMonitorToScreenMode.clear();
   gPFData.ModesPerMonitor.clear();

   // For each monitor
   const int MonitorCount = iPlatform::GetMonitorCount();
   for ( tMonitorIndex MonitorIndex = 0; MonitorIndex < MonitorCount; ++MonitorIndex )
   {
      tScreenModeIndex ScreenModeCount = 0;

      // Get its system index
      tSystemMonitorIndex SystemMonitorIndex = gPFData.MonitorIndexToSystemMonitorIndex[MonitorIndex];
      // Using its index, get its device name
      // Get the structure that will be used to call the function.
      DISPLAY_DEVICE Device = { 0 };
      // Set it up so it's ready for use.
      Device.cb = sizeof( DISPLAY_DEVICE );
      EnumDisplayDevices( NULL, SystemMonitorIndex, &Device, 0 );

      //
      DEVMODE DeviceMode;
      DeviceMode.dmSize = sizeof( DEVMODE );
      DeviceMode.dmDriverExtra = 0;

      // For each mode of this monitor:
      tSystemScreenModeIndex SystemScreenModeIndex = 0;
      while ( EnumDisplaySettings( Device.DeviceName, SystemScreenModeIndex, &DeviceMode ) )
      {
         // Initialize the data structure
         ScreenModeInfo Mode;

         Mode.bitsPerPixel = static_cast<uint8_t>( DeviceMode.dmBitsPerPel );
         Mode.dimensions[kHeight] = DeviceMode.dmPelsHeight;
         Mode.dimensions[kWidth] = DeviceMode.dmPelsWidth;
         Mode.refreshRate = static_cast<uint8_t>( DeviceMode.dmDisplayFrequency );
         // Verify it's supported
         // If so:
         if ( IsSupportedScreenMode( Mode ) )
         {
            // Add it to the map
            tModeAndMonitor ModeAndMonitor;
            ModeAndMonitor.first = ScreenModeCount;
            ModeAndMonitor.second = MonitorIndex;
            gPFData.ModeAndMonitorToSystemScreenModeIndex[ModeAndMonitor] = SystemScreenModeIndex;
            gPFData.ModeAndMonitorToScreenMode[ModeAndMonitor] = Mode;
            ++ScreenModeCount;
         }
         ++SystemScreenModeIndex;
      }
      gPFData.ModesPerMonitor[MonitorIndex] = ScreenModeCount;
   }

   return true;
}

bool InitializePixelFormatsList()
{
   gPFData.SupportedPixelFormats.clear();
   gPFData.SupportedPixelFormatsToSystemPixelFormats.clear();

   // In order to access pixel formats, we need to have an OpenGL rendering
   // context.
   ScopedOpenGLContext ScopedContext;
   ScopedContext.Init();
   HDC DeviceContextHandle = ScopedContext.GetDC();

   const uint32_t FormatCount = static_cast<uint32_t>( DescribePixelFormat( DeviceContextHandle, 1, 0, NULL ) );

   // For the wgl function:
   static PFNWGLGETPIXELFORMATATTRIBIVARBPROC wglGetPixelFormatAttribivARB = NULL;

   if ( wglGetPixelFormatAttribivARB == NULL )
   {
      wglGetPixelFormatAttribivARB = ( PFNWGLGETPIXELFORMATATTRIBIVARBPROC )
                                     wglGetProcAddress( "wglGetPixelFormatAttribivARB" );
   }

   // If we can use the extension, then go for it:
   if ( wglGetPixelFormatAttribivARB != NULL )
   {
      for ( tSystemPixelFormatIndex i = 0; i < FormatCount; ++i )
      {
         PixelFormat pf;

         int attributeInfo[15];

         // These are the attributes that concern us:
         int attributes [15] = { WGL_SUPPORT_OPENGL_ARB, // 0
                                 WGL_ACCELERATION_ARB,   // 1
                                 WGL_DRAW_TO_WINDOW_ARB, // 2
                                 WGL_DOUBLE_BUFFER_ARB,  // 3
                                 WGL_PIXEL_TYPE_ARB,     // 4
                                 WGL_DEPTH_BITS_ARB,     // 5
                                 WGL_STENCIL_BITS_ARB,   // 6
                                 WGL_SAMPLE_BUFFERS_ARB, // 7
                                 WGL_SAMPLES_ARB,        // 8
                                 WGL_ACCUM_BITS_ARB,     // 9
                                 WGL_RED_BITS_ARB,       // 10
                                 WGL_GREEN_BITS_ARB,     // 11
                                 WGL_BLUE_BITS_ARB,      // 12
                                 WGL_ALPHA_BITS_ARB,     // 13
                                 WGL_COLOR_BITS_ARB      // 14
                               };

         // Get the pixel format information.
         wglGetPixelFormatAttribivARB( DeviceContextHandle, i, 0, 16, attributes, attributeInfo );

         pf.bitPerColor[kR] = static_cast<uint8_t>( attributeInfo[10] );
         pf.bitPerColor[kG] = static_cast<uint8_t>( attributeInfo[11] );
         pf.bitPerColor[kB] = static_cast<uint8_t>( attributeInfo[12] );
         pf.bitPerColor[kA] = static_cast<uint8_t>( attributeInfo[13] );

         pf.bitsPerPixel = static_cast<uint8_t>( attributeInfo[14] );
         pf.accumBufferBits = static_cast<uint8_t>( attributeInfo[9] );
         pf.depthBufferBits = static_cast<uint8_t>( attributeInfo[5] );

         pf.stencilBits = static_cast<uint8_t>( attributeInfo[6] );

         if ( attributeInfo[3] == 1 )
         {
            pf.doubleBuffered = true;
         }

         if ( attributeInfo[0] == 1 && attributeInfo[2] == 1 )
         {
            pf.glWindowSupported = true;
         }

         switch ( attributeInfo[1] )
         {
         case WGL_FULL_ACCELERATION_ARB:
            pf.acceleration = kFull;
            break;

         case WGL_GENERIC_ACCELERATION_ARB:
            pf.acceleration = kPartial;
            break;

         case WGL_NO_ACCELERATION_ARB:
            pf.acceleration = kNone;
            break;

         default:
            break;
         }

         if ( attributeInfo[4] == WGL_TYPE_RGBA_ARB )
         {
            pf.RGBA = true;
         }

         if ( attributeInfo[7] == 1 )              // Multisampled
         {
            pf.multisampleMode = static_cast<uint8_t>( attributeInfo[8] );
         }

         if ( IsSupportedPixelFormat( i, pf, ScopedContext ) )
         {
            const tPixelFormatIndex PFIndex = gPFData.SupportedPixelFormats.size();
            gPFData.SupportedPixelFormats.push_back( pf );
            gPFData.SupportedPixelFormatsToSystemPixelFormats[PFIndex] = i;
         }
      }
   }
   else
   {
      for ( uint32_t i = 0; i < FormatCount; ++i )
      {
         PIXELFORMATDESCRIPTOR pfd;
         pfd.nVersion = 1;
         pfd.nSize = sizeof( pfd );

         DescribePixelFormat( DeviceContextHandle, i, sizeof( PIXELFORMATDESCRIPTOR ), &pfd );

         // If the generic format flag is clear, then this format is fully accelerated.
         PixelFormat pf;
         if ( !( pfd.dwFlags & PFD_GENERIC_FORMAT ) )
         {
            pf.acceleration = kFull;
         }
         else
         {
            // If this flag is set, then we have a bare minimum of features.
            if ( pfd.dwFlags & PFD_GENERIC_ACCELERATED )
            {
               pf.acceleration = kPartial;
            }
            // Otherwise, we have nothing.
            else
            {
               pf.acceleration = kNone;
            }
         }

         pf.accumBufferBits = pfd.cAccumBits;
         pf.bitPerColor[kR] = pfd.cRedBits;
         pf.bitPerColor[kG] = pfd.cGreenBits;
         pf.bitPerColor[kB] = pfd.cBlueBits;
         pf.bitPerColor[kA] = pfd.cAlphaBits;
         pf.bitsPerPixel = pfd.cColorBits;
         pf.depthBufferBits = pfd.cDepthBits;

         if ( pfd.dwFlags & PFD_DOUBLEBUFFER )
         {
            pf.doubleBuffered = true;
         }

         pf.multisampleMode = 0;
         pf.stencilBits = pfd.cStencilBits;

         if ( ( pfd.dwFlags & PFD_SUPPORT_OPENGL ) && ( pfd.dwFlags & PFD_DRAW_TO_WINDOW ) )
         {
            pf.glWindowSupported = true;
         }

         if ( pfd.iPixelType == PFD_TYPE_RGBA )
         {
            pf.RGBA = true;
         }

         if ( IsSupportedPixelFormat( i, pf, ScopedContext ) )
         {
            const tPixelFormatIndex PFIndex = gPFData.SupportedPixelFormats.size();
            gPFData.SupportedPixelFormats.push_back( pf );
            gPFData.SupportedPixelFormatsToSystemPixelFormats[PFIndex] = i;
         }
      }
   }

   ScopedContext.ReleaseDC( DeviceContextHandle );

   if ( gPFData.SupportedPixelFormats.size() > 0 )
   {
      return true;
   }
   return false;
}

void InitializeControllersList()
{
   gPFData.ControllerInfos.clear();

   const tControllerIndex MaxNumControllers = joyGetNumDevs();
   JOYCAPS CurJoyCaps;
   const uint32_t JoyCapsSize = sizeof( CurJoyCaps );
   for ( tControllerIndex i = 0; i < MaxNumControllers; ++i )
   {
      JOYINFO JoyInfo;

      if ( joyGetPos( i, &JoyInfo ) == JOYERR_NOERROR )
      {
         joyGetDevCaps( i, &CurJoyCaps, JoyCapsSize );

         ControllerInfo Info;
         Info.NumButtons = CurJoyCaps.wNumButtons;
         Info.NumAxes = CurJoyCaps.wNumAxes;
         Info.Hat = CurJoyCaps.wCaps & JOYCAPS_HASPOV ? true : false;

         gPFData.ControllerInfos.push_back( Info );
      }
   }
}

bool gPlatformInitialized = false;
bool Initialize()
{
   gPlatformInitialized = true;

   // Monitor infos much be initialized before screen modes
   if ( InitializeMonitorInfosList() == false )
   {
      return false;
   }
   if ( InitializeScreenModesList() == false )
   {
      return false;
   }
   if ( InitializePixelFormatsList() == false )
   {
      return false;
   }
   InitializeControllersList();

   return true;
}

void MaybeInitialize()
{
   if ( gPlatformInitialized )
   {
      return;
   }

   Initialize();
}

bool SetScreenMode( tScreenModeIndex ScreenModeIndex, tMonitorIndex MonitorIndex )
{
   MaybeInitialize();

   // Get the system screen mode index for this monitor and screen mode
   tModeAndMonitor ModeAndMonitor;
   ModeAndMonitor.first = ScreenModeIndex;
   ModeAndMonitor.second = MonitorIndex;
   tSystemScreenModeIndex SystemScreenModeIndex = gPFData.ModeAndMonitorToSystemScreenModeIndex[ModeAndMonitor];

   tSystemMonitorIndex SystemMonitorIndex = gPFData.MonitorIndexToSystemMonitorIndex[MonitorIndex];

   // Get the monitor device information
   DISPLAY_DEVICE Device = { 0 };
   // Set it up so it's ready for use.
   Device.cb = sizeof( DISPLAY_DEVICE );
   EnumDisplayDevices( NULL, SystemMonitorIndex, &Device, 0 );

   // Get the devicemode using the device name
   DEVMODE NewDeviceMode;
   NewDeviceMode.dmSize = sizeof( NewDeviceMode );
   NewDeviceMode.dmDriverExtra = 0;
   EnumDisplaySettings( Device.DeviceName, SystemScreenModeIndex, &NewDeviceMode );

   DEVMODE CurrentDevMode;
   CurrentDevMode.dmSize = sizeof( CurrentDevMode );
   CurrentDevMode.dmDriverExtra = 0;
   EnumDisplaySettings( Device.DeviceName, ENUM_CURRENT_SETTINGS, &CurrentDevMode );

   // If anything we care about isn't the same, set the new mode
   if ( CurrentDevMode.dmBitsPerPel != NewDeviceMode.dmBitsPerPel ||
         CurrentDevMode.dmDisplayFrequency != NewDeviceMode.dmDisplayFrequency ||
         CurrentDevMode.dmPelsHeight != NewDeviceMode.dmPelsHeight ||
         CurrentDevMode.dmPelsWidth != NewDeviceMode.dmPelsWidth )
   {
      // Change the screen resolution (temporarily, will revert after the application exits)
      return ChangeDisplaySettingsEx( Device.DeviceName, &NewDeviceMode, NULL, CDS_FULLSCREEN, NULL ) == DISP_CHANGE_SUCCESSFUL;
   }
   return true;
}

void SetActiveContext( const tWindowID WindowID )
{
   gPFData.Windows[WindowID]->ActivateContext();
}

void SwapBuffers( const tWindowID WindowID )
{
   gPFData.Windows[WindowID]->SwapBuffers();
}

eMessageResults CreateOSDialog( const char * const Title, const char * const Message,
                                const eMessageTypes Type, const eMessageOptions Options,
                                const tWindowID WindowID )
{
   UINT OSMessageType = MB_ICONINFORMATION;
   if ( Type == kWarningMessage )
   {
      OSMessageType = MB_ICONWARNING;
   }
   else if ( Type == kErrorMessage )
   {
      OSMessageType = MB_ICONERROR;
   }

   switch ( Options )
   {
   case kConfirm:
      OSMessageType |= MB_OK;
      break;

   case kConfirmCancel:
      OSMessageType |= MB_OKCANCEL;
      break;

   case kYesNo:
      OSMessageType |= MB_YESNO;
      break;

   case kYesNoCancel:
      OSMessageType |= MB_YESNOCANCEL;
      break;

   case kRetryCancel:
      OSMessageType |= MB_RETRYCANCEL;
      break;

   case kCancelRetryContinue:
      OSMessageType |= MB_CANCELTRYCONTINUE;
      break;

   default:
      OSMessageType |= MB_OK;
      break;
   }

   HWND WindowHandle = NULL;
   if ( WindowID != kNull8 )
   {
      WindowHandle = gPFData.Windows[WindowID]->GetHandle();
   }

   ShowCursor( TRUE );
   UINT OSMessageResult = MessageBox( WindowHandle, Message, Title, OSMessageType );
   ShowCursor( FALSE );

   switch ( OSMessageResult )
   {
   case IDOK:
      return kMessageConfirm;

   case IDYES:
      return kMessageYes;

   case IDNO:
      return kMessageNo;

   case IDCANCEL:
      // Fall through
   case IDABORT:
      return kMessageCancel;

   case IDCONTINUE:
      // Fall through
   case IDIGNORE:
      return kMessageContinue;

   case IDTRYAGAIN:
      // Fall through
   case IDRETRY:
      return kMessageRetry;

   default:
      return kMessageConfirm;
   }
}

std::string GetFontDirectory()
{
   // Create the font list
   char * Path = new char[MAX_PATH];
   memset( Path, 0, MAX_PATH );

   SHGetFolderPath( NULL, CSIDL_FONTS, NULL, 0, Path );

   std::string FontDirectory = Path;

   delete[] Path;

   return FontDirectory;
}

std::list<std::string> GetDirectoryFilePaths( const char * const Directory, const char * const ExclusiveExtension )
{
   std::string SearchPath = Directory;
   // If we have an extension to search only for:
   if ( ExclusiveExtension != NULL )
   {
      // Add a wildcard
      SearchPath += "/*";
      // If the extension passed in doesn't include a dot, add one
      if ( ExclusiveExtension[0] != '.' )
      {
         SearchPath += '.';
      }
      // Add the extension
      SearchPath += ( ExclusiveExtension );
   }

   // Create a list of paths to files in the current directory matching the pattern
   std::list<std::string> Filepaths;
   // Try finding the first file
   WIN32_FIND_DATA FindData;
   HANDLE FileHandle = FindFirstFile( SearchPath.c_str(), &FindData );
   // If we succeeded:
   if ( FileHandle != INVALID_HANDLE_VALUE )
   {
      // Add this first file as a found file
      Filepaths.push_back( Directory );
      Filepaths.back() += '/';
      Filepaths.back() += FindData.cFileName;

      // Keep finding new files until there are no more
      while ( FindNextFile( FileHandle, &FindData ) )
      {
         Filepaths.push_back( Directory );
         Filepaths.back() += '/';
         Filepaths.back() += FindData.cFileName;
      }

      // Close the file handle opened by the search
      FindClose( FileHandle );
   }
   return Filepaths;
}

bool IsKeyboardKeyDown( const eKeyboardKey Which )
{
   switch ( Which )
   {
   case KK_Escape:
   {
      return ( bool )( GetAsyncKeyState( VK_ESCAPE ) & 0x8000 );
   }
   break;
   case KK_F1:
   {
      return ( bool )( GetAsyncKeyState( VK_F1 ) & 0x8000 );
   }
   break;
   case KK_F2:
   {
      return ( bool )( GetAsyncKeyState( VK_F2 ) & 0x8000 );
   }
   break;
   case KK_F3:
   {
      return ( bool )( GetAsyncKeyState( VK_F3 ) & 0x8000 );
   }
   break;
   case KK_F4:
   {
      return ( bool )( GetAsyncKeyState( VK_F4 ) & 0x8000 );
   }
   break;
   case KK_F5:
   {
      return ( bool )( GetAsyncKeyState( VK_F5 ) & 0x8000 );
   }
   break;
   case KK_F6:
   {
      return ( bool )( GetAsyncKeyState( VK_F6 ) & 0x8000 );
   }
   break;
   case KK_F7:
   {
      return ( bool )( GetAsyncKeyState( VK_F7 ) & 0x8000 );
   }
   break;
   case KK_F8:
   {
      return ( bool )( GetAsyncKeyState( VK_F8 ) & 0x8000 );
   }
   break;
   case KK_F9:
   {
      return ( bool )( GetAsyncKeyState( VK_F9 ) & 0x8000 );
   }
   break;
   case KK_F10:
   {
      return ( bool )( GetAsyncKeyState( VK_F10 ) & 0x8000 );
   }
   break;
   case KK_F11:
   {
      return ( bool )( GetAsyncKeyState( VK_F11 ) & 0x8000 );
   }
   break;
   case KK_F12:
   {
      return ( bool )( GetAsyncKeyState( VK_F12 ) & 0x8000 );
   }
   break;
   case KK_Tilde:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_3 ) & 0x8000 );
   }
   break;
   case KK_0:
   {
      return ( bool )( GetAsyncKeyState( '0' ) & 0x8000 );
   }
   break;
   case KK_1:
   {
      return ( bool )( GetAsyncKeyState( '1' ) & 0x8000 );
   }
   break;
   case KK_2:
   {
      return ( bool )( GetAsyncKeyState( '2' ) & 0x8000 );
   }
   break;
   case KK_3:
   {
      return ( bool )( GetAsyncKeyState( '3' ) & 0x8000 );
   }
   break;
   case KK_4:
   {
      return ( bool )( GetAsyncKeyState( '4' ) & 0x8000 );
   }
   break;
   case KK_5:
   {
      return ( bool )( GetAsyncKeyState( '5' ) & 0x8000 );
   }
   break;
   case KK_6:
   {
      return ( bool )( GetAsyncKeyState( '6' ) & 0x8000 );
   }
   break;
   case KK_7:
   {
      return ( bool )( GetAsyncKeyState( '7' ) & 0x8000 );
   }
   break;
   case KK_8:
   {
      return ( bool )( GetAsyncKeyState( '8' ) & 0x8000 );
   }
   break;
   case KK_9:
   {
      return ( bool )( GetAsyncKeyState( '9' ) & 0x8000 );
   }
   break;
   case KK_Dash:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_MINUS ) & 0x8000 );
   }
   break;
   case KK_Equals:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_PLUS ) & 0x8000 );
   }
   break;
   case KK_BackSpace:
   {
      return ( bool )( GetAsyncKeyState( VK_BACK ) & 0x8000 );
   }
   break;
   case KK_A:
   {
      return ( bool )( GetAsyncKeyState( 'A' ) & 0x8000 );
   }
   break;
   case KK_B:
   {
      return ( bool )( GetAsyncKeyState( 'B' ) & 0x8000 );
   }
   break;
   case KK_C:
   {
      return ( bool )( GetAsyncKeyState( 'C' ) & 0x8000 );
   }
   break;
   case KK_D:
   {
      return ( bool )( GetAsyncKeyState( 'D' ) & 0x8000 );
   }
   break;
   case KK_E:
   {
      return ( bool )( GetAsyncKeyState( 'E' ) & 0x8000 );
   }
   break;
   case KK_F:
   {
      return ( bool )( GetAsyncKeyState( 'F' ) & 0x8000 );
   }
   break;
   case KK_G:
   {
      return ( bool )( GetAsyncKeyState( 'G' ) & 0x8000 );
   }
   break;
   case KK_H:
   {
      return ( bool )( GetAsyncKeyState( 'H' ) & 0x8000 );
   }
   break;
   case KK_I:
   {
      return ( bool )( GetAsyncKeyState( 'I' ) & 0x8000 );
   }
   break;
   case KK_J:
   {
      return ( bool )( GetAsyncKeyState( 'J' ) & 0x8000 );
   }
   break;
   case KK_K:
   {
      return ( bool )( GetAsyncKeyState( 'K' ) & 0x8000 );
   }
   break;
   case KK_L:
   {
      return ( bool )( GetAsyncKeyState( 'L' ) & 0x8000 );
   }
   break;
   case KK_M:
   {
      return ( bool )( GetAsyncKeyState( 'M' ) & 0x8000 );
   }
   break;
   case KK_N:
   {
      return ( bool )( GetAsyncKeyState( 'N' ) & 0x8000 );
   }
   break;
   case KK_O:
   {
      return ( bool )( GetAsyncKeyState( 'O' ) & 0x8000 );
   }
   break;
   case KK_P:
   {
      return ( bool )( GetAsyncKeyState( 'P' ) & 0x8000 );
   }
   break;
   case KK_Q:
   {
      return ( bool )( GetAsyncKeyState( 'Q' ) & 0x8000 );
   }
   break;
   case KK_R:
   {
      return ( bool )( GetAsyncKeyState( 'R' ) & 0x8000 );
   }
   break;
   case KK_S:
   {
      return ( bool )( GetAsyncKeyState( 'S' ) & 0x8000 );
   }
   break;
   case KK_T:
   {
      return ( bool )( GetAsyncKeyState( 'T' ) & 0x8000 );
   }
   break;
   case KK_U:
   {
      return ( bool )( GetAsyncKeyState( 'U' ) & 0x8000 );
   }
   break;
   case KK_V:
   {
      return ( bool )( GetAsyncKeyState( 'V' ) & 0x8000 );
   }
   break;
   case KK_W:
   {
      return ( bool )( GetAsyncKeyState( 'W' ) & 0x8000 );
   }
   break;
   case KK_X:
   {
      return ( bool )( GetAsyncKeyState( 'X' ) & 0x8000 );
   }
   break;
   case KK_Y:
   {
      return ( bool )( GetAsyncKeyState( 'Y' ) & 0x8000 );
   }
   break;
   case KK_Z:
   {
      return ( bool )( GetAsyncKeyState( 'Z' ) & 0x8000 );
   }
   break;
   case KK_Tab:
   {
      return ( bool )( GetAsyncKeyState( VK_TAB ) & 0x8000 );
   }
   break;
   case KK_LeftBracket:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_4 ) & 0x8000 );
   }
   break;
   case KK_RightBracket:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_6 ) & 0x8000 );
   }
   break;
   case KK_BackSlash:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_5 ) & 0x8000 );
   }
   break;
   case KK_CapsLock:
   {
      return ( bool )( GetAsyncKeyState( VK_CAPITAL ) & 0x8000 );
   }
   break;
   case KK_Colon:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_1 ) & 0x8000 );
   }
   break;
   case KK_Quote:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_7 ) & 0x8000 );
   }
   break;
   case KK_Return:
   {
      return ( bool )( GetAsyncKeyState( VK_RETURN ) & 0x8000 );
   }
   break;
   case KK_LeftShift:
   {
      return ( bool )( GetAsyncKeyState( VK_LSHIFT ) & 0x8000 );
   }
   break;
   case KK_Comma:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_COMMA ) & 0x8000 );
   }
   break;
   case KK_Period:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_PERIOD ) & 0x8000 );
   }
   break;
   case KK_Slash:
   {
      return ( bool )( GetAsyncKeyState( VK_OEM_2 ) & 0x8000 );
   }
   break;
   case KK_RightShift:
   {
      return ( bool )( GetAsyncKeyState( VK_RSHIFT ) & 0x8000 );
   }
   break;
   case KK_LeftCtrl:
   {
      return ( bool )( GetAsyncKeyState( VK_LCONTROL ) & 0x8000 );
   }
   break;
   case KK_LeftWindows:
   {
      return ( bool )( GetAsyncKeyState( VK_LWIN ) & 0x8000 );
   }
   break;
   case KK_LeftAlt:
   {
      return ( bool )( GetAsyncKeyState( VK_LMENU ) & 0x8000 );
   }
   break;
   case KK_Space:
   {
      return ( bool )( GetAsyncKeyState( VK_SPACE ) & 0x8000 );
   }
   break;
   case KK_RightAlt:
   {
      return ( bool )( GetAsyncKeyState( VK_RMENU ) & 0x8000 );
   }
   break;
   case KK_RightWindows:
   {
      return ( bool )( GetAsyncKeyState( VK_RWIN ) & 0x8000 );
   }
   break;
   case KK_ContextMenu:
   {
      return ( bool )( GetAsyncKeyState( VK_APPS ) & 0x8000 );
   }
   break;
   case KK_RightCtrl:
   {
      return ( bool )( GetAsyncKeyState( VK_RCONTROL ) & 0x8000 );
   }
   break;

   // Cursor keys column of the keyboard
   case KK_PrintScreen:
   {
      return ( bool )( GetAsyncKeyState( VK_SNAPSHOT ) & 0x8000 );
   }
   break;
   case KK_ScrollLock:
   {
      return ( bool )( GetAsyncKeyState( VK_SCROLL ) & 0x8000 );
   }
   break;
   case KK_PauseBreak:
   {
      return ( bool )( GetAsyncKeyState( VK_PAUSE ) & 0x8000 );
   }
   break;
   case KK_Insert:
   {
      return ( bool )( GetAsyncKeyState( VK_INSERT ) & 0x8000 );
   }
   break;
   case KK_Home:
   {
      return ( bool )( GetAsyncKeyState( VK_HOME ) & 0x8000 );
   }
   break;
   case KK_PageUp:
   {
      return ( bool )( GetAsyncKeyState( VK_PRIOR ) & 0x8000 );
   }
   break;
   case KK_Delete:
   {
      return ( bool )( GetAsyncKeyState( VK_DELETE ) & 0x8000 );
   }
   break;
   case KK_End:
   {
      return ( bool )( GetAsyncKeyState( VK_END ) & 0x8000 );
   }
   break;
   case KK_PageDown:
   {
      return ( bool )( GetAsyncKeyState( VK_NEXT ) & 0x8000 );
   }
   break;
   case KK_Up:
   {
      return ( bool )( GetAsyncKeyState( VK_UP ) & 0x8000 );
   }
   break;
   case KK_Left:
   {
      return ( bool )( GetAsyncKeyState( VK_LEFT ) & 0x8000 );
   }
   break;
   case KK_Down:
   {
      return ( bool )( GetAsyncKeyState( VK_DOWN ) & 0x8000 );
   }
   break;
   case KK_Right:
   {
      return ( bool )( GetAsyncKeyState( VK_RIGHT ) & 0x8000 );
   }
   break;

   // Numeric Keypad column of the keyboard
   case KK_NumLock:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMLOCK ) & 0x8000 );
   }
   break;
   case KK_NumForwardSlash:
   {
      return ( bool )( GetAsyncKeyState( VK_DIVIDE ) & 0x8000 );
   }
   break;
   case KK_Asterisk:
   {
      return ( bool )( GetAsyncKeyState( VK_MULTIPLY ) & 0x8000 );
   }
   break;
   case KK_Minus:
   {
      return ( bool )( GetAsyncKeyState( VK_SUBTRACT ) & 0x8000 );
   }
   break;
   case KK_Num0:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD0 ) & 0x8000 );
   }
   break;
   case KK_Num1:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD1 ) & 0x8000 );
   }
   break;
   case KK_Num2:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD2 ) & 0x8000 );
   }
   break;
   case KK_Num3:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD3 ) & 0x8000 );
   }
   break;
   case KK_Num4:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD4 ) & 0x8000 );
   }
   break;
   case KK_Num5:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD5 ) & 0x8000 );
   }
   break;
   case KK_Num6:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD6 ) & 0x8000 );
   }
   break;
   case KK_Num7:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD7 ) & 0x8000 );
   }
   break;
   case KK_Num8:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD8 ) & 0x8000 );
   }
   break;
   case KK_Num9:
   {
      return ( bool )( GetAsyncKeyState( VK_NUMPAD9 ) & 0x8000 );
   }
   break;
   case KK_NumPlus:
   {
      return ( bool )( GetAsyncKeyState( VK_ADD ) & 0x8000 );
   }
   break;
   case KK_NumPeriod:
   {
      return ( bool )( GetAsyncKeyState( VK_DECIMAL ) & 0x8000 );
   }
   break;
   case KK_NumEnter:
   {
      Warning( "Numpad enter can't be queried on windows, sorry." );
      return false;
   }
   break;
   default:
      Warning( "Key not found in pf::IsKeyboardKeyDown." );
      return false;
   }
}

float GetDesktopGamma()
{
   return 1.0f;
}
}
